package ru.kest.torrentwatchlist.dao;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import ru.kest.torrentwatchlist.entity.Movie;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.*;
import java.util.Date;
import java.util.List;

/**
 * Movie DAO
 *
 * Created by kkharitonov on 28.11.2014.
 */
@Repository
public class MovieDAO {

    private static Logger logger = Logger.getLogger("MovieDAO");

    @PersistenceContext
    private EntityManager entityManager;

    @Transactional
    public List<Movie> findAllMovies() {

/*
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Movie> cq = cb.createQuery(Movie.class);
        cq.select(cq.from(Movie.class));
        TypedQuery<Movie> q = entityManager.createQuery(cq);

*/
        TypedQuery<Movie> q = entityManager.createQuery("SELECT DISTINCT m FROM Movie m JOIN FETCH m.torrents t WHERE m.skipped = FALSE ORDER BY m.foundDate DESC ", Movie.class);

        List<Movie> movies = q.getResultList();

        updateViewDate(movies);

        return movies;
    }

    private void updateViewDate(List<Movie> movies) {
        for (Movie movie : movies) {
            if (movie.getViewingDate() == null) {
                movie.setViewingDate(new Date());
                save(movie);
            }
        }
    }

    @Transactional
    public void mergeMovieToDB(Movie originalMovie) {

        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Movie> cq = cb.createQuery(Movie.class);

//        Metamodel m = entityManager.getMetamodel();
//        EntityType<Movie> mov_ = m.entity(Movie.class);

        Root<Movie> movieRoot = cq.from(Movie.class);
//        Predicate where = cb.and()
        Predicate where = null;
        if (!StringUtils.isEmpty(originalMovie.getImdbId())) {
            where = cb.equal(movieRoot.get("imdbId"), originalMovie.getImdbId());
        }
        if (!StringUtils.isEmpty(originalMovie.getKinopoiskId())) {
            if (where == null) {
                where = cb.equal(movieRoot.get("kinopoiskId"), originalMovie.getKinopoiskId());
            } else {
                where = cb.or(where, cb.equal(movieRoot.get("kinopoiskId"), originalMovie.getKinopoiskId()));
            }
        }

        // TODO if movie has no imdb and kinopoisk IDs - need to find by movie name
        cq.where(where);
        TypedQuery<Movie> q = entityManager.createQuery(cq);
//        Query query = entityManager.getCriteriaBuilder().createQuery("from SecureMessage sm where sm.someField=:arg1");
//        query.setParameter("arg1", arg1);
        List<Movie> sameMovies = q.getResultList();
        if (sameMovies.size() > 0) {
            logger.debug("!!! find another record in DB!");
            for(Movie movie : sameMovies) {
                originalMovie.merge(movie);
            }
        }

        save(originalMovie);
    }

    @Transactional
    public void save(Movie movie) {
        if (movie.getId() == null) {
            entityManager.persist(movie);
        } else {
            entityManager.merge(movie);
        }
    }

    @Transactional
    public void setFavorites(int id, Boolean value) {
        updateField(id, "favorites", value);
    }

    @Transactional
    public void setSkipped(int id) {
        updateField(id, "skipped", Boolean.TRUE);
    }

    private void updateField(int id, String fieldName, Object value) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaUpdate<Movie> updateCriteria = cb.createCriteriaUpdate(Movie.class);
        Root<Movie> root = updateCriteria.from(Movie.class);
        updateCriteria.set(root.get(fieldName), value);
// set where clause
        updateCriteria.where(cb.equal(root.get("id"), id));
// update
        int affected = entityManager.createQuery(updateCriteria).executeUpdate();
        logger.info("ID: " + id + "; Field " + fieldName + " updated by value " + value + "; Result: " + affected);
    }

    @Transactional
    public void removeAll() {
        entityManager.createQuery("DELETE FROM Torrent t").executeUpdate();
        entityManager.createQuery("DELETE FROM Movie m").executeUpdate();
    }
}
